#include "lvgl/lvgl.h"
#include <stdio.h>

#include "config/CustomConfig.h"
#include "audio_play_ui.h"
#include "../data_handle/data_handle.h"
#include "utils/player/audio_player.h"
#include "utils/components/slider_component.h"
#include <SDL2/SDL.h>
#include "utils/components/lv_obj_util.h"
// 报错❌：error: implicit declaration of function 'SDL_GetTicks' [-Wimplicit-function-declaration]
//    54 |         music_start_ticks = SDL_GetTicks();
// 原因分析✅：在使用 SDL_GetTicks() 时，编译器还不知道它的声明。SDL_GetTicks() 是 SDL.h 提供的一个函数，用于获取自程序启动以来经过的毫秒数。
// 解决办法✅：需引用声明SDL_GetTicks()的头文件：#include <SDL2/SDL.h>

// 音乐播放器页面audio player screen
lv_obj_t * audio_play_screen; // 在.h通过extern声明audio_play_screen但不分配内存，然后在.c文件即此处给audio_play_screen分配内存
// 创建方式	                 是否屏幕对象	    是否自动释放	  是否需要手动删除	    备注
// lv_scr_act()	            ✅ 当前活动屏幕	  ❌ 否	          通常不删（系统管理）	 LVGL 启动时默认创建
// lv_obj_create(NULL)	    ✅ 自定义屏幕	  ❌ 否	          ✅ 需要	         适合多屏切换
// lv_obj_create(parent)	❌ 普通控件	     ✅ 随父对象释放	   ❌ 不需手动删	        受父控件管理

//Q: 1️⃣printf与2️⃣fprintf的核心区别：
// 1️⃣printf打印格式化文本到 标准输出（stdout），一般是终端屏幕
// 2️⃣fprintf(FILE *stream, …)，打印格式化文本到 指定的文件流，可以是 stdout、stderr 或文件指针
// printf("1️⃣ Hello stdout (same as fprintf(stdout, ...))\n");
// FILE *file = fopen("out.txt", "w");
// fprintf(file, "4️⃣ This goes into out.txt\n");

// ✅printf() 是对 fprintf(stdout, …) 的一个封装
// stdout （终端时）	    行缓冲	  每遇到 \n 自动刷新，如果没有换行符就不会输出到终端
// stdout （被重定向时）	全缓冲	   直到缓冲区满或 fflush(stdout)，否则不会输出到终端
// stderr	              无缓冲	 立即输出，不等待换行符

static lv_obj_t *play_label; //播放按钮标题
static lv_obj_t * slider_label;  //创建标签用于显示slider当前数值
Uint32 music_start_millisecs = 0;//计时播放多少毫秒，已播放毫秒数
Uint32 music_duration = 125;//音乐时长多少秒，可用tag或外部获取
static lv_obj_t * music_slider;
lv_timer_t *slider_timer;//lvgl 定时器，用来定时每100毫秒更新一次slider播放进度
int tiem_period_ms = 100;//lvgl 定时器间隔时间，单位毫秒

static _Bool is_playing = false;//是否正在播放，1是true情况下while(is_playing)不断调用update_slider()更新播放进度
// 用函数来修改 is_playing实现间接“监听”is_playing变量发生变化，并在函数中处理“状态变化逻辑”
void set_is_playing(_Bool playing)
{
    if (is_playing == playing) return; // 值没变，不处理

    is_playing = playing;
    // is_playing值发生变化
    if (is_empty_player()) {
        printf("⏹ 无音乐内容，slider禁用\n");
        config_enable_slider(music_slider, false);
    } else {
        printf("🎵 有音乐内容，slider启用\n");
        config_enable_slider(music_slider, true);
    }
}


static void back_btn_event_handler(lv_event_t * e)
{
    printf("btn_event_handler\n");
    stopPlayingMusic();
    lv_screen_load(data_handle_screen);//返回上一页data_handle_screen屏幕
    // lv_scr_load()只是将活动屏幕切换为新的screen，把旧屏幕从显示链上移除（不显示），但不会删除旧屏幕对象本身
    // 因此如果不再需要audio_play_screen必须手动释放，否则它会一直占用内存：
    lv_obj_del(audio_play_screen);
}
static void play_finished_callback() {
    printf("audio_play_ui play_finished_callback\n");
    //播放完成，重置为Play
    lv_label_set_text(play_label, "Play");
    set_is_playing(false);//播放完成，重置是否正在播放为false即停止更新music_slider的while(is_playing)循环
}

/**
 * 更新进度条和播放进度
 * @param timer lvgl 定时器
 */
void update_slider(lv_timer_t *timer) {
    //每100毫秒调用一次update_slider，因此已播放毫秒数此处加100毫秒
    music_start_millisecs += tiem_period_ms;
    if (lv_slider_is_dragged(music_slider)) {//用户正在拖动滑块时，不要覆盖用户的操作
        return;
    }
    if (!is_playing) {
        // 未在播放，删除定时器
        lv_timer_del(timer);  // slider_timer 是 lv_timer_t*，之前创建的定时器
        timer = NULL;          // 避免悬空指针
        return;
    }
    Uint32 music_start_secs = (Uint32)(music_start_millisecs / 1000.0);
    config_slider_progress(music_slider, slider_label, music_start_secs, music_duration);
}
void play_audio() {
    if (is_empty_player()) {
        char *mp3_file_relative_path = "assets/audio/SmileThree.mp3";
        single_play_mp3_with_callback(mp3_file_relative_path, play_finished_callback);
        lv_label_set_text(play_label, "Pause");
        music_start_millisecs = 0;  // 重置已播放秒数
        set_is_playing(true);

        // ✅在 LVGL 中：所有 UI 操作（包括更新进度条）都必须在 LVGL 主线程或 lv_timer_handler() 调用的上下文内执行
        // ❌否则UI更新无效，因此此处在播放时创建LVGL定时器lv_timer来更新 UI
        // ✅启动 LVGL 定时器lv_timer，每100ms更新一次slider，一直循环调用回调直到删除定时器如下：
        // lv_timer_del(slider_timer);  // slider_timer 是 lv_timer_t*，之前创建的定时器
        // slider_timer = NULL;          // 避免悬空指针
        slider_timer = lv_timer_create(update_slider, tiem_period_ms, NULL);
    } else if (isPausedMusic())
    {
        set_is_playing(true);
        lv_label_set_text(play_label, "Pause");
        resumePlayingMusic();
        slider_timer = lv_timer_create(update_slider, tiem_period_ms, NULL);
    } else {
        set_is_playing(false);
        lv_label_set_text(play_label, "Play");
        pausePlayingMusic();
    }
}
static void volume_slider_value_changed_event_cb(lv_event_t * e)
{
    lv_obj_t * volume_slider = lv_event_get_target(e);
    int slider_value = (int)lv_slider_get_value(volume_slider);
    printf("config_volume_with_value: %d\n",slider_value);
    config_volume_with_value(slider_value);
}
static void slider_value_changed_event_cb(lv_event_t * e)
{
    if (!isPlayingMusic())
    {
        //未在播放音乐
        printf("Music isn't playing\n");
        return;
    }
    char buf[5];//缓冲区，最长情况为100%四个字符再加上结束符\0共5个字符即最长5个字符，此处缓冲区设置5个字符就够用了
    /*把获取的滑块值写入buf中*/
    int slider_value = (int)lv_slider_get_value(music_slider);
    lv_snprintf(buf, sizeof(buf), "%d%%", slider_value);
	/*把buf中的内容显示在label上*/
    lv_label_set_text(slider_label, buf);
}
static void slider_touch_up_released_event(lv_event_t * e)
{
    if (!isPlayingMusic())
    {
        printf("Music isn't playing\n");
        //未在播放音乐，拖拽进度条无效，slider重置回0
        lv_slider_set_value(music_slider, 0, LV_ANIM_OFF);
        return;
    }
    //用于获取触发事件的对象（控件）。这个函数通常在事件处理函数中使用，以便获取触发事件的控件，并根据需要进行相应的处理。
    music_slider = lv_event_get_target(e);
    char buf[5];//缓冲区，最长情况为100%四个字符再加上结束符\0共5个字符即最长5个字符，此处缓冲区设置5个字符就够用了
    /*把获取的滑块值写入buf中*/
    int slider_value = (int)lv_slider_get_value(music_slider);
    lv_snprintf(buf, sizeof(buf), "%d%%", slider_value);
	/*把buf中的内容显示在label上*/
    lv_label_set_text(slider_label, buf);
    // lv_obj_align_to(slider_label, music_slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
    // lv_obj_align_to 使一个控件其相对于另一个控件或屏幕边缘进行对齐。位置是在底部的中间，Y偏移10


    // slider 的范围是 0~100，对应播放长度的百分比
    double new_progress = slider_value / 100.0;
    double position = new_progress * music_duration;
    music_start_millisecs = (Uint32)position * 1000;//* 1000转为毫秒

    int seek_result = Mix_SetMusicPosition(position);//设置音乐播放位置，播放到第几秒，单位秒，pause暂停播放情况也能设置但设置后不会自动播放
    //成功返回0，失败返回 -1
    //❌若无播放的音乐返回-1失败，Mix_GetError()为"Music isn't playing"
    if (seek_result == -1) {
        printf("不支持该格式的seek: %s\n", Mix_GetError());
    } else {
        printf("支持该格式的seek\n");
    }
}

void audio_play_ui(void) {
    //NULL参数：表示创建一个独立的屏幕对象，没有父对象。
    audio_play_screen = lv_obj_create(NULL);
    lv_obj_t *label1 = lv_label_create(audio_play_screen);
    lv_label_set_text(label1, "This is Audio Player Screen");
    lv_obj_align(label1, LV_ALIGN_TOP_MID, 0, 32);

    lv_obj_t *btn = lv_button_create(audio_play_screen);
    lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 16, 16);

    lv_obj_t *btn_label = lv_label_create(btn);
    lv_label_set_text(btn_label, "< Back");
    lv_obj_center(btn_label);// 设置元素位置为父视图正中间，整体居中对齐

    lv_obj_add_event_cb(btn, back_btn_event_handler, LV_EVENT_CLICKED, NULL);

    lv_obj_t *play_btn = lv_button_create(audio_play_screen);
    lv_obj_align(play_btn, LV_ALIGN_CENTER, 0, 0);

    play_label = lv_label_create(play_btn);
    lv_label_set_text(play_label, "Play");
    lv_obj_center(play_label);

    lv_obj_add_event_cb(play_btn, play_audio, LV_EVENT_CLICKED, NULL);

    //Q: ✅调用lv_screen_load方法把这个audio_play_screen设置为当前活动屏幕替换掉原来的data_handle_screen主屏幕而不是遮挡而已
    // 此时调用lv_scr_act()方法会返回该audio_play_screen
    // data_handle_screen是一个指针，指向当前活动屏幕对象，被替换掉当前活动屏幕后不会变成NULL即不会变，
    // 也不会自动释放（除非手动调用lv_obj_del(data_handle_screen)），它仍然指向原来的屏幕对象，只是这个屏幕不再是活动屏幕
    lv_screen_load(audio_play_screen);

    /*在当前屏幕创建music_slider*/
    music_slider = lv_slider_create(lv_scr_act());
    config_enable_slider(music_slider, false);
    lv_obj_align_to(music_slider, play_label, LV_ALIGN_OUT_BOTTOM_MID, 0, 16);
    //lv_obj_add_event_cb：cb = callback（回调函数），为某个事件event添加回调方法
    //LV_EVENT_VALUE_CHANGED：添加当slider值发生变化时即滑块被拖动或者按键被改变时回调方法slider_event_cb
    //拖拽滑块过程中会一直不断发送LV_EVENT_VALUE_CHANGED信号即不断触发回调方法。
    lv_obj_add_event_cb(music_slider, slider_touch_up_released_event, LV_EVENT_RELEASED, NULL);
    //slider值发生变化时触发回调函数，用于更新label标签文本内容显示拖拽到哪了
    lv_obj_add_event_cb(music_slider, slider_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

    slider_label = lv_label_create(lv_scr_act());           //在滑动条下创建标签
    lv_label_set_text(slider_label, "0%");                  //设置标签初始值为0%
    lv_obj_align_to(slider_label, music_slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
    // lv_obj_align_to 使一个控件其相对于另一个控件或屏幕边缘进行对齐。位置是在底部的中间，Y偏移10

    /*在当前屏幕创建volume_slider*/
    int32_t volume_slider_w = 160;
    lv_obj_t * volume_slider = lv_slider_create(lv_scr_act());
    lv_obj_set_size(volume_slider, volume_slider_w, 10);  // 宽 160，高 10
    // 布局设置volume_slider右侧距离lv_scr_act()右侧16px，与music_slider垂直居中
    // 获取music_slider的宽度，如果返回0则需要调用lv_obj_update_layout强制重新计算坐标
    lv_coord_t obj_w = get_obj_width(music_slider);
    int right_padding = 16;//距离屏幕右侧距离
    int32_t offset_x = (DISP_WIDTH - obj_w) / 2 - (int)volume_slider_w - right_padding;
    lv_obj_align_to(volume_slider, music_slider, LV_ALIGN_OUT_RIGHT_MID, offset_x, 0);

    // 添加slider值变动的事件监听
    lv_obj_add_event_cb(volume_slider, volume_slider_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);


    LV_IMG_DECLARE(sound);

    // 音量图标
    lv_obj_t *sound_img = lv_image_create(lv_scr_act());
    lv_obj_update_layout(volume_slider);
    lv_obj_align_to(sound_img, volume_slider, LV_ALIGN_OUT_LEFT_MID, -16, 0);
    lv_obj_set_size(sound_img, 30, 30);
}
